<?php

namespace App\Services;

use App\Common\Libs\Ali\Oss;
use App\Common\Libs\ArrayHelper;
use App\Common\Libs\IdBuilder;
use App\Consts\ExceptionCode;
use App\Consts\GlobalArticleConst;
use App\Consts\GlobalCacheConst;
use App\Consts\GlobalCollectConst;
use App\Consts\GlobalConst;
use App\Consts\GlobalCounterConst;
use App\Consts\GlobalDynamicConst;
use App\Consts\GlobalPackageConst;
use App\Consts\GlobalSpiderConst;
use App\Consts\GlobalUploadConst;
use App\Consts\GlobalUserRecommendConst;
use App\Exceptions\Error;
use App\Models\Cache\Cache;
use App\Models\Cache\CacheKey;
use App\Models\IndexSearchCollect;
use App\Models\SpiderInfo;
use App\Models\SpiderOriginal;
use App\Models\SysArticleOriginalUrl;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;

/**
 * @package App\Services
 * @see QuickService
 */
class QuickService extends BaseService
{
    private $_repository;

    public function __construct()
    {
        parent::__construct();
        $this->_repository = repository()->CollectRepository;
    }

    /**
     * 一键收藏 - 预览
     * @param $params
     * @return mixed
     */
    public function onPreview($params)
    {
        // 校验参数
        $validateResult = $this->validateParams($params);
        if (!$validateResult) {
            return $this->throwError(ExceptionCode::ILLEGAL_PARAMETER, lang('app.Url not right'));
        }

        // 文章已经被平台删除，或已经收藏则不能被收藏
        $canNotCollected = $this->judgeCanNotCollected($params['url'], $params['user_id']);
        if ($canNotCollected) {
            return $canNotCollected;
        }

        // 平台有文章，且已经收藏，直接返回
        $articleOriginalUrl = SysArticleOriginalUrl::getModel(md5($params['url']))
            ->where('original_url', $params['url'])
            ->first();
        if ($articleOriginalUrl) {
            $article = $this->ArticleService->getArticleByArticleId($articleOriginalUrl->article_id);
            if ($article) {
                $isCollected = $this->CollectService->judgeCollected($params['user_id'], $article->article_id);
                if ($isCollected == GlobalCollectConst::USER_IS_COLLECTED) {
                    return Error::handle(lang('Repeat quick collect'));
                }
            }
        }

        DB::beginTransaction();
        try {
            // 查看缓存中是否存在链接对应的信息
            $cacheArticle = Cache::getInstance([
                'url' => $params['url']
            ])->hGetAll(CacheKey::ONE_CLICK_COLLECT_SPIDER);
            if ($cacheArticle) {
                return [
                    'title' => !empty($cacheArticle['title']) ? $cacheArticle['title'] : '',
                    'cover_url' => !empty($cacheArticle['cover_url']) ? config('alioss.ImageUrl') . $cacheArticle['cover_url'] : '',
                    'description' => !empty($cacheArticle['description']) ? $cacheArticle['description'] : str_replace(PHP_EOL, '', trim(mb_substr(strip_tags($cacheArticle['content']), 0, 162))),
                ];
            }

            // 通过链接查看[spider_info]表中是否存在记录
            $articleOriginalUrl = SysArticleOriginalUrl::getModel(md5($params['url']))
                ->where('original_url', $params['url'])
                ->first();
            if ($articleOriginalUrl) {
                $article = $this->ArticleService->getArticleByArticleId($articleOriginalUrl->article_id);
                if ($article) {
                    return [
                        'id' => $article->article_id,
                        'title' => $article->title,
                        'cover_url' => $article->cover_url,
                        'description' => $article->description,
                    ];
                }
            }

            // 抓取文章
            $spiderResult = $this->catchArticle($params['url'], $params['user_id']);
            if (!$spiderResult) {
                return $this->throwError(ExceptionCode::ILLEGAL_PARAMETER, lang('app.Network error'));
            }

            // 结果如缓存
            Cache::getInstance([
                'url' => $params['url']
            ])->hset(CacheKey::ONE_CLICK_COLLECT_SPIDER, $spiderResult, GlobalCacheConst::EXPIRED_ONE_HOURS);

            // 记录抓取日志
            $this->LogSpiderService->addByConsole([
                'user_id' => $params['user_id'],
                'url' => $params['url'],
            ]);

            DB::commit();

            $description = trim(mb_substr(strip_tags($spiderResult['content']), 0, 162));
            $description = str_replace(PHP_EOL, '', $description);

            return [
                'title' => $spiderResult['title'],
                'cover_url' => config('alioss.ImageUrl') . $spiderResult['cover_url'],
                'description' => $description,
            ];
        } catch (\Exception $e) {
            DB::rollBack();
            // 记录抓取日志
            $this->LogSpiderService->addByConsole([
                'user_id' => $params['user_id'],
                'url' => $params['url'],
            ]);
            return $this->throwError(ExceptionCode::CODE_FAIL, '', $e->getMessage());
        }
    }

    /**
     * 一键收藏 - 预览后收藏
     * @param $params
     * @return array|bool|\Illuminate\Http\JsonResponse|mixed
     */
    public function onCollect($params)
    {
        $spiderInfo = SpiderInfo::getModel()
            ->where('url', $params['url'])
            ->first();
        // 平台没有文章
        if (!$spiderInfo) {
            // 查看缓存中是否存在链接对应的信息
            $cacheArticle = Cache::getInstance([
                'url' => $params['url']
            ])->hGetAll(CacheKey::ONE_CLICK_COLLECT_SPIDER);
            if ($cacheArticle) {
                return $this->collectArticleByInfo(ArrayHelper::merge($cacheArticle, [
                    'original_url' => $params['url'],
                    'package_id' => $params['package_id'],
                    'is_recommend' => $params['is_recommend'],
                ]), $params['user_id']);
            }

            // 缓存没有，重新抓取
            $spiderResult = $this->catchArticle($params['url'], $params['user_id']);
            if (!$spiderResult) {
                return $this->throwError(ExceptionCode::ILLEGAL_PARAMETER, lang('app.Network error'));
            }

            return $this->collectArticleByInfo(ArrayHelper::merge($spiderResult, $params), $params['user_id']);
        }

        // 平台有文章
        $articleOriginalUrl = SysArticleOriginalUrl::getModel(md5($params['url']))
            ->where('original_url', $params['url'])
            ->first();
        if (!$articleOriginalUrl) {
            return Error::handle();
        }

        $article = $this->ArticleService->getArticleByArticleId($articleOriginalUrl->article_id);
        if (!$article) {
            return Error::handle();
        }

        // 已经收藏，更换收藏夹
        $isCollected = $this->CollectService->judgeCollected($params['user_id'], $article->article_id);
        if ($isCollected == GlobalCollectConst::USER_IS_COLLECTED) {
            service()->CollectService->addCollect([
                'user_id' => $params['user_id'],
                'target_id' => $article->article_id,
                'target_type' => GlobalConst::TARGET_TYPE_ARTICLE,
                'package_id' => $params['package_id']
            ]);
            return [
                'id' => $article->article_id,
                'title' => $article->title,
                'cover_url' => $article->cover_url,
                'description' => $article->description,
                'target_type' => GlobalConst::TARGET_TYPE_ARTICLE,
                'is_collect' => GlobalCollectConst::USER_IS_COLLECTED,
                'is_deleted' => GlobalConst::IS_NOT_DELETE,
                'is_shield' => 0,
                'original_url' => $params['url'],
                'created_at' => time(),
            ];
        }

        // 没有收藏，进行收藏
        return $this->collectArticleByInfo(ArrayHelper::merge(ArrayHelper::toArray($article), [
            'is_recommend' => $params['is_recommend'],
            'spider_original_id' => $article->spider_original_id,
            'original_url' => $params['url'],
            'package_id' => $params['package_id'],
            'content' => $article->content,
            'url' => $params['url'],
            'article_id' => $article->article_id,
            'account_name' => $spiderInfo->account_name
        ]), $params['user_id']);
    }

    public function onCatch($params)
    {
        $author = repository()->UserRepository->getUserByPhone($params['phone']);
        if (!$author) {
            return Error::handle(lang('User is not exists'));
        }
        DB::beginTransaction();
        try {
            $articleOriginalUrl = SysArticleOriginalUrl::getModel(md5($params['url']))
                ->where('original_url', $params['url'])
                ->first();
            /**
             * 文章存在
             *  已经是原创直接返回
             *  修改作者ID、修改为原创
             */
            if ($articleOriginalUrl) {
                $article = $this->ArticleService->getArticleByArticleId($articleOriginalUrl->article_id);
                if ($article->type == GlobalArticleConst::ARTICLE_TYPE_ORIGINAL) {
                    return Error::handle(lang('Article is already original'));
                }
                $article = repository()->ArticleRepository->updateArticle($article, [
                    'author_id' => $author->user_id,
                    'type' => GlobalArticleConst::ARTICLE_TYPE_ORIGINAL
                ]);
                if (!$article) {
                    return Error::handle();
                }
                repository()->ArticleRepository->updateArticleIndex($article->index, [
                    'author_id' => $author->user_id,
                    'type' => GlobalArticleConst::ARTICLE_TYPE_ORIGINAL
                ]);
                return $article->commonFormat();
            }
            // 文章不存在
            $spiderResult = service()->SpiderService->getSpiderScriptResult($params['url']);
            if (!$spiderResult) {
                return Error::handle(lang('Spider script is error'));
            }
            $articleInfo = ArrayHelper::merge($spiderResult, [
                'article_id' => IdBuilder::getUniqueID(),
                'author_id' => $author->user_id,
            ]);
            $articleInfo['description'] = trim(mb_substr(strip_tags($articleInfo['content']), 0, 162));
            $articleInfo['description'] = str_replace(PHP_EOL, '', $articleInfo['description']);
            Oss::getInstance(GlobalUploadConst::OBJECT_ARTICLE_INFO_PATH)->uploadTarget([
                'id' => $articleInfo['article_id'],
                'title' => $articleInfo['title'],
                'cover_url' => $articleInfo['cover_url'],
                'description' => $articleInfo['description'] ?? '',
                'content' => $articleInfo['content'],
            ]);
            // 新增[spider_info]表记录
            SpiderInfo::getModel()->create([
                'spider_original_id' => $articleInfo['spider_original_id'],
                'url' => $articleInfo['url'],
                'title' => $articleInfo['title'],
                'cover_url' => $articleInfo['cover_url'],
                'author_name' => $articleInfo['author_name'],
                'account_name' => $articleInfo['account_name'] ?? '',
                'original_created_at' => $articleInfo['original_created_at'],
            ]);

            // 新增[sys_article_]表记录
            $article = repository()->ArticleRepository->create([
                'article_id' => $articleInfo['article_id'],
                'author_id' => $articleInfo['author_id'],
                'spider_original_id' => $articleInfo['spider_original_id'],
                'type' => GlobalArticleConst::ARTICLE_TYPE_ORIGINAL,
                'original_url' => $articleInfo['url'],
                'cover_url' => $articleInfo['cover_url'],
                'title' => $articleInfo['title'],
                'description' => $articleInfo['description'],
                'original_created_at' => $articleInfo['original_created_at'],
            ]);
            if (!$article) {
                return false;
            }

            // 新增[sys_article_content_]表记录
            $articleContent = repository()->ArticleRepository->createContent([
                'article_id' => $articleInfo['article_id'],
                'content' => $articleInfo['content']
            ]);
            if (!$articleContent) {
                return false;
            }

            // 【暂时】新增[index_article]表记录
            $indexArticle = repository()->ArticleRepository->createIndex([
                'article_id' => $articleInfo['article_id'],
                'author_id' => $articleInfo['author_id'],
                'spider_original_id' => $articleInfo['spider_original_id'],
                'type' => GlobalArticleConst::ARTICLE_TYPE_ORIGINAL,
            ]);
            if (!$indexArticle) {
                return false;
            }

            // 新增[sys_article_original_]表记录
            $articleOriginalUrl = repository()->ArticleRepository->createOriginalUrl([
                'article_id' => $articleInfo['article_id'],
                'user_id' => $articleInfo['author_id'],
                'original_url' => $articleInfo['url'],
            ]);
            if (!$articleOriginalUrl) {
                return false;
            }

            // 新增[sys_article_user_]表记录
            $articleUser = repository()->ArticleRepository->createArticleUser([
                'article_id' => $articleInfo['article_id'],
                'user_id' => $articleInfo['author_id'],
            ]);
            if (!$articleUser) {
                return false;
            }

            // 同步插入[spider_content]表记录，防止执行异步图片替换时，参数过长
            $this->SpiderService->createSpiderContent([
                'url' => $articleInfo['original_url'],
                'content' => '',
                'original_content' => $articleInfo['content']
            ]);

            DB::commit();

            $queueMessageBody = [
                'user_id' => $author->user_id,
                'type' => GlobalDynamicConst::DYNAMIC_TYPE_ORIGINAL_ARTICLE,
                'target_id' => $article->article_id,
                'target_type' => GlobalConst::TARGET_TYPE_ARTICLE,
            ];
            $this->DynamicService->addDynamicByTarget($queueMessageBody);

            // 异步替换封面提、正文图片上传到第三方图片仓库
            $this->ArticleService->asyncRepairImage($articleInfo);

            return $article->commonFormat();
        } catch (\Exception $e) {
            dd($e->getMessage());
            DB::rollBack();
            return $this->throwError();
        }
    }

    private function validateParams($params)
    {
        // 验证参数
        if (empty($params['url'])) {
            return false;
        }

        // 验证链接是否在我们的识别范围内，能识别返回执行脚本path
        $url = parse_url(strtolower($params['url']));
        if (!$url || empty($url['host'])) {
            // 记录抓取日志
            $this->LogSpiderService->addByConsole([
                'user_id' => $params['user_id'],
                'url' => $params['url'],
            ]);
            return false;
        }

        // 验证链接是否符合要求
        $spiderOriginal = SpiderOriginal::getInstance()
            ->select(['id'])
            ->where(['base_url' => $url['host']])
            ->where(['status' => GlobalSpiderConst::STATUS_NORMAL, 'is_deleted' => GlobalConst::IS_NOT_DELETE])
            ->first();

        if (!$spiderOriginal) {
            // 记录抓取日志
            $this->LogSpiderService->addByConsole([
                'user_id' => $params['user_id'],
                'url' => $url,
            ]);
            return false;
        }

        return true;
    }

    private function judgeCanNotCollected($url, $userId)
    {
        $model = SysArticleOriginalUrl::getModel($url)
            ->where('original_url', $url)
            ->first();
        if (!$model) {
            return false;
        }

        // 校验文章是否已经被平台删除
        if ($model->article->is_deleted == GlobalConst::IS_DELETED) {
            return $this->throwError(ExceptionCode::ILLEGAL_PARAMETER, lang('app.This article not allow collect'));
        }

        // 文章是否被用户收藏
        $collectedRes = $this->CollectService->judgeCollected($userId, $model->article_id);
        if ($collectedRes == GlobalCollectConst::USER_IS_COLLECTED) {
            return $this->throwError(ExceptionCode::ILLEGAL_PARAMETER, lang('app.Do not repeat quick collect article'));
        }

        return false;
    }

    private function catchArticle($url, $userId)
    {
        return $this->SpiderService->getSpiderScriptResult($url, $userId);
    }

    private function collectArticleByInfo($articleInfo, $userId)
    {
        // 校验收藏夹是否存在
        $package = $this->PackageService->getCollectPackageById($articleInfo['package_id']);
        if (!$package) {
            return $this->throwError();
        }

        $articleInfo['author_id'] = $userId;
        $articleInfo['description'] = trim(mb_substr(strip_tags($articleInfo['content']), 0, 162));
        $articleInfo['description'] = str_replace(PHP_EOL, '', $articleInfo['description']);
        $articleInfo['article_id'] = $articleInfo['article_id'] ?? IdBuilder::getUniqueID();

        $coverUrl = $articleInfo['cover_url'];
        if ($articleInfo['cover_url'] && strpos($articleInfo['cover_url'], 'http') !== false) {
            $coverUrl = config('alioss.ImageUrl') . $articleInfo['cover_url'];
        } elseif ($articleInfo['cover_url'] && strpos($articleInfo['cover_url'], '//') !== false) {
            // 预防这种链接：//image.woshipm.com/wp-files/img/50.jpg
            $host = strpos($articleInfo['cover_url'], ':') !== false ? 'http' : 'http:';
            $coverUrl = config('alioss.ImageUrl') . $host . $articleInfo['cover_url'];
        }


        DB::beginTransaction();
        try {
            // [spider_info]表有记录说明已经是文章了，该用户不算这篇文章的首次发布者
            $spiderInfo = SpiderInfo::getModel()
                ->where('url', $articleInfo['original_url'])
                ->first();
            if ($spiderInfo) {
                // 新增[sys_article_user_]表记录
                $articleUser = repository()->ArticleRepository->createArticleUser([
                    'article_id' => $articleInfo['article_id'],
                    'user_id' => $articleInfo['author_id'],
                ]);
                if (!$articleUser) {
                    return false;
                }

                service()->RecommendSystemService->addTarget([
                    'target_id' => $articleInfo['article_id'],
                    'target_type' => GlobalConst::NEW_TARGET_TYPE_QUICK_RECOMMEND_ARTICLE,
                    'user_id' => $userId
                ]);
            } else {
                Oss::getInstance(GlobalUploadConst::OBJECT_ARTICLE_INFO_PATH)->uploadTarget([
                    'id' => $articleInfo['article_id'],
                    'title' => $articleInfo['title'],
                    'cover_url' => $articleInfo['cover_url'],
                    'description' => $articleInfo['description'] ?? '',
                    'content' => $articleInfo['content'],
                ]);
                // 新增[spider_info]表记录
                SpiderInfo::getModel()->create([
                    'spider_original_id' => $articleInfo['spider_original_id'],
                    'url' => $articleInfo['url'],
                    'title' => $articleInfo['title'],
                    'cover_url' => $articleInfo['cover_url'],
                    'author_name' => $articleInfo['author_name'],
                    'account_name' => $articleInfo['account_name'] ?? '',
                    'original_created_at' => $articleInfo['original_created_at'],
                ]);

                // 新增[sys_article_]表记录
                $article = repository()->ArticleRepository->create([
                    'article_id' => $articleInfo['article_id'],
                    'author_id' => $articleInfo['author_id'],
                    'spider_original_id' => $articleInfo['spider_original_id'],
                    'original_url' => $articleInfo['url'],
                    'cover_url' => $articleInfo['cover_url'],
                    'title' => $articleInfo['title'],
                    'description' => $articleInfo['description'],
                    'original_created_at' => $articleInfo['original_created_at'],
                ]);
                if (!$article) {
                    return false;
                }

                // 新增[sys_article_content_]表记录
                $articleContent = repository()->ArticleRepository->createContent([
                    'article_id' => $articleInfo['article_id'],
                    'content' => $articleInfo['content']
                ]);
                if (!$articleContent) {
                    return false;
                }

                // 【暂时】新增[index_article]表记录
                $indexArticle = repository()->ArticleRepository->createIndex([
                    'article_id' => $articleInfo['article_id'],
                    'author_id' => $articleInfo['author_id'],
                    'spider_original_id' => $articleInfo['spider_original_id'],
                ]);
                if (!$indexArticle) {
                    return false;
                }

                // 新增[sys_article_original_]表记录
                $articleOriginalUrl = repository()->ArticleRepository->createOriginalUrl([
                    'article_id' => $articleInfo['article_id'],
                    'user_id' => $articleInfo['author_id'],
                    'original_url' => $articleInfo['url'],
                ]);
                if (!$articleOriginalUrl) {
                    return false;
                }

                // 新增[sys_article_user_]表记录
                $articleUser = repository()->ArticleRepository->createArticleUser([
                    'article_id' => $articleInfo['article_id'],
                    'user_id' => $articleInfo['author_id'],
                ]);
                if (!$articleUser) {
                    return false;
                }

                // 同步插入[spider_content]表记录，防止执行异步图片替换时，参数过长
                $this->SpiderService->createSpiderContent([
                    'url' => $articleInfo['original_url'],
                    'content' => '',
                    'original_content' => $articleInfo['content']
                ]);
            }

            // 更新收藏夹total数
            $collectPackage = repository()->PackageRepository->getCollectPackageById($articleInfo['package_id']);
            if ($collectPackage) {
                $total = $collectPackage->total + 1;

                repository()->PackageRepository->updateCollectPackage($collectPackage, [
                    'total' => $total
                ]);
            }

            // 同步插入[user_collect_]表
            $collect = service()->CollectService->addCollect([
                'collect_id' => IdBuilder::getUniqueID(),
                'user_id' => $userId,
                'package_id' => $articleInfo['package_id'],
                'target_id' => $articleInfo['article_id'],
                'target_type' => GlobalConst::TARGET_TYPE_ARTICLE,
            ]);
            if ($collect === true) {
                return [
                    'id' => $articleInfo['article_id'],
                    'target_type' => GlobalConst::TARGET_TYPE_ARTICLE,
                    'title' => $articleInfo['title'],
                    'cover_url' => $coverUrl,
                    'description' => $articleInfo['description'],
                    'is_collect' => GlobalCollectConst::USER_IS_COLLECTED,
                    'is_deleted' => GlobalConst::IS_NOT_DELETE,
                    'is_shield' => 0,
                    'original_url' => $articleInfo['original_url'],
                    'created_at' => time(),
                ];
            }
            if ($collect && !($collect instanceof JsonResponse)) {
                $searCollect = IndexSearchCollect::getModel();
                $searCollect->create([
                    'collect_id' => $collect['collect_id'],
                    'search_title' => $articleInfo['title'],
                    'user_id' => $userId,
                ]);
            }

            // 同步更新用户的收藏数、推荐数
            $countArr = $package->is_public == GlobalPackageConst::IS_PUBLIC ? [GlobalCounterConst::COUNT_COLLECT => 1,
                GlobalCounterConst::COUNT_USER_RECOMMEND => 1] : GlobalCounterConst::COUNT_COLLECT;
            Cache::getInstance([
                'article_id' => $articleInfo['article_id'],
            ])->incrFromArray(CacheKey::COUNTER_ARTICLE, $countArr, GlobalCounterConst::INCREASE);
            Cache::getInstance([
                'user_id' => $userId,
            ])->incrFromArray(CacheKey::COUNTER_USER, $countArr, GlobalCounterConst::INCREASE);

            // 收到藏到公开收藏夹后：1.队列生成动态，2.生成提交记录
            if ($package->is_public == GlobalPackageConst::IS_PUBLIC) {
                $queueMessageBody = [
                    'user_id' => $userId,
                    'type' => GlobalDynamicConst::DYNAMIC_TYPE_USER_RECOMMEND_ARTICLE,
                    'target_id' => $articleInfo['article_id'],
                    'target_type' => GlobalConst::TARGET_TYPE_ARTICLE,
                ];
                $this->DynamicService->addDynamicByTarget($queueMessageBody);
            }

            DB::commit();
            if ($articleInfo['is_recommend'] == GlobalUserRecommendConst::USER_IS_RECOMMEND) {
                // 同步插入[user_recommend_article_]表
                $userRecommend = $this->UserRecommendService->createUserRecommend([
                    'user_id' => $userId,
                    'target_id' => $articleInfo['article_id'],
                    'target_type' => GlobalConst::TARGET_TYPE_ARTICLE,
                ]);
                if (!is_bool($userRecommend)) {
                    $this->UserRecommendService->saveUserRecommendLogCounterByQueue($userRecommend);
                }
            }

            if (!$spiderInfo) {
                // 异步替换封面提、正文图片上传到第三方图片仓库
                $this->ArticleService->asyncRepairImage($articleInfo);
            }

            $result = [
                'id' => $articleInfo['article_id'],
                'target_type' => GlobalConst::TARGET_TYPE_ARTICLE,
                'title' => $articleInfo['title'],
                'cover_url' => $coverUrl,
                'description' => $articleInfo['description'],
                'is_collect' => GlobalCollectConst::USER_IS_COLLECTED,
                'is_deleted' => GlobalConst::IS_NOT_DELETE,
                'is_shield' => 0,
                'original_url' => $articleInfo['original_url'],
                'created_at' => time(),
            ];
            return $this->getResult($result);
        } catch (\Exception $e) {
            DB::rollBack();
            return Error::throw($e);
        }
    }
}
